#include "bootpack.h"
#include <stdio.h>

#define KEYCMD_LED      0xed

void keywin_on(struct SHEET *key_win);
void keywin_off(struct SHEET *key_win);
void close_shtask(struct TASK *task);
void close_shel(struct SHEET *sht);

void HariMain(void)
{
    struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
	struct SHTCTL *shtctl;
    struct FIFO32 fifo, keycmd;
    char s[40];
	int fifobuf[128], keycmd_buf[32];
    int mx, my, data, new_mx = -1, new_my = 0, new_wx = 0x7fffffff, new_wy = 0;
    unsigned int memtotal;
    struct MOUSE_DEC mdec;
    struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR; 
    unsigned char *buf_back, buf_mouse[256];
    struct SHEET *sht_back, *sht_mouse;
    struct TASK *task_a, *task;
	static char keytable0[0x80] = {
		0,   0,   '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '^', 0x08,  0,
		'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '@', '[', 0x0a, 0,  'A', 'S',
		'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', ':', 0,   0,   ']', 'Z', 'X', 'C', 'V',
		'B', 'N', 'M', ',', '.', '/', 0,   '*', 0,   ' ', 0,   0,   0,   0,   0,   0,
		0,   0,   0,   0,   0,   0,   0,   '7', '8', '9', '-', '4', '5', '6', '+', '1',
		'2', '3', '0', '.', 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
		0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
		0,   0,   0,   0x5c, 0,  0,   0,   0,   0,   0,   0,   0,   0,   0x5c, 0,  0
	};
    static char keytable1[0x80] = {
		0,   0,   '!', 0x22, '#', '$', '%', '&', 0x27, '(', ')', '~', '=', '~', 0x08, 0,
		'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '`', '{', 0x0a, 0,   'A', 'S',
		'D', 'F', 'G', 'H', 'J', 'K', 'L', '+', '*', 0,   0,   '}', 'Z', 'X', 'C', 'V',
		'B', 'N', 'M', '<', '>', '?', 0,   '*', 0,   ' ', 0,   0,   0,   0,   0,   0,
		0,   0,   0,   0,   0,   0,   0,   '7', '8', '9', '-', '4', '5', '6', '+', '1',
		'2', '3', '0', '.', 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
		0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
		0,   0,   0,   '_', 0,   0,   0,   0,   0,   0,   0,   0,   0,   '|', 0,   0
	};
    int key_shift = 0, key_leds = (binfo->leds >> 4) & 7, keycmd_wait = -1;
    int i, x, y, mmx = -1, mmy = -1, mmx2 = 0;    // mm = move mode
    struct SHEET *sht = 0, *key_win;

    init_gdtidt();
    init_pic();
    io_sti();
    fifo32_init(&fifo, 128, fifobuf, TASK_NULL);
    *((int *) 0x0fec) = (int) &fifo;
    init_pit();
    init_keyboard(&fifo, 256);
    enable_mouse(&fifo, 512, &mdec);
    io_out8(PIC0_IMR, 0xf8);
    io_out8(PIC1_IMR, 0xef);
    fifo32_init(&keycmd, 32, keycmd_buf, TASK_NULL);

    memtotal = memtest(0x00400000, 0xbfffffff);
    memman_init(memman);
    memman_free(memman, 0x00001000, 0x0009e000);
    memman_free(memman, 0x00400000, memtotal - 0x00400000);

    init_palette();
    shtctl = shtctl_init(memman, binfo->vram, binfo->scrnx, binfo->scrny);
	task_a = task_init(memman);
    fifo.task = task_a;
    task_run(task_a, 1, 2);
    *((int *) 0x0fe4) = (int) shtctl;

    /* sht_back */
    sht_back = sheet_alloc(shtctl);
    buf_back = (unsigned char *) memman_alloc_4k(memman, binfo->scrnx * binfo->scrny);
    sheet_setbuf(sht_back, buf_back, binfo->scrnx, binfo->scrny, -1);
    init_screen8(buf_back, binfo->scrnx, binfo->scrny);

    /* sht_shel */
    key_win = open_shell(shtctl, memtotal);

    /* sht_mouse */
    sht_mouse = sheet_alloc(shtctl);
    sheet_setbuf(sht_mouse, buf_mouse, 16, 16, 99);
    init_mouse_cursor8(buf_mouse, 99);
    mx = (binfo->scrnx - 16) / 2;	//计算画面中心坐标
    my = (binfo->scrny - 28 - 16) / 2;

    sheet_slide(sht_back, 0 , 0);
    sheet_slide(key_win, 32, 4);
    sheet_slide(sht_mouse, mx, my);
    sheet_updown(sht_back, 0);
    sheet_updown(key_win, 1);
    sheet_updown(sht_mouse, 2);
    keywin_on(key_win);

/*
 *   sprintf(s, "(%3d, %3d)", mx, my);
 *   putfonts8_asc_sht(sht_back, 0, 0, COL8_FFFFFF, COL8_008484, s, 10);
 *   sprintf(s, "memory %dMB   free : %dKB", 
 *           memtotal / (1024 * 1024), memman_total(memman) / 1024);
 *   putfonts8_asc_sht(sht_back, 0, 32, COL8_FFFFFF, COL8_008484, s, 40);
 */

    /* 为了避免和键盘冲突,在一开始进行设置 */
    fifo32_set(&keycmd, KEYCMD_LED);
    fifo32_set(&keycmd, key_leds);

    while (1) {
        if (fifo32_status(&keycmd) > 0 && keycmd_wait < 0)
        {
            keycmd_wait = fifo32_get(&keycmd);
            wait_KBC_sendready();
            io_out8(PORT_KEYDAT, keycmd_wait);
        }
        io_cli();
        if (fifo32_status(&fifo) == 0) {
            /* FIFO为空,当存在搁置的绘图操作时立刻执行 */
            if (new_mx >= 0)
            {
                io_sti();
                sheet_slide(sht_mouse, new_mx, new_my);
                new_mx = -1;
            }
            else if (new_wx != 0x7fffffff)
            {
                io_sti();
                sheet_slide(sht, new_wx, new_wy);
                new_wx = 0x7fffffff;
            }
            else
            {
                task_sleep(task_a);
                io_sti();
            }
        }
        else {
            data = fifo32_get(&fifo);
            io_sti();
            if (key_win != 0 && key_win->flags == 0)    //输入窗口被关闭
            {
                if (shtctl->top == 1)   //当画面只剩下背景和鼠标时
                    key_win = 0;
                else
                {
                    key_win = shtctl->sheets[shtctl->top - 1];
                    keywin_on(key_win);
                }
            }
            if (256 <= data && data <= 511) /* 键盘数据 */
            {
                if (data < 0x80 + 256)
                {
                    if (key_shift == 0)
                        s[0] = keytable0[data - 256];
                    else
                        s[0] = keytable1[data - 256];
                }
                else
                {
                    s[0] = 0;
                }
                if ('A' <= s[0] && s[0] <= 'Z') /* 大写字母 */
                {
                    if (((key_leds & 4) == 0 && key_shift == 0) ||
                        ((key_leds & 4) != 0 && key_shift != 0))
                    {
                        s[0] += 0x20;
                    }
                }
                if (s[0] != 0 && key_win != 0)    /* common char, enter, bakespace */
                {
                    fifo32_set(&key_win->task->fifo,s[0] + 256);
                }
                if (data == 256 + 0x0f && key_win != 0)  /* tab */
                {
                    keywin_off(key_win);
                    i = key_win->height - 1;
                    if (i == 0)
                        i = shtctl->top - 1;
                    key_win = shtctl->sheets[i];
                    keywin_on(key_win);
                }
                if (data == 256 + 0x57 && shtctl->top > 2)  /* F11 */
                    sheet_updown(shtctl->sheets[1], shtctl->top - 1);
                if (data == 256 + 0x2a) /* left shift on */
                    key_shift |= 1;
                if (data == 256 + 0x36) /* right shift on */
                    key_shift |= 2;
                if (data == 256 + 0xaa) /* left shift off */
                    key_shift &= ~1;
                if (data == 256 + 0xb6) /* right shift off */
                    key_shift &= ~2;
                if (data == 256 + 0x3a) /* Caps Lock */
                {
                    key_leds ^= 4;
                    fifo32_set(&keycmd, KEYCMD_LED);
                    fifo32_set(&keycmd, key_leds);
                }
                if (data == 256 + 0x45) /* Num Lock */
                {
                    key_leds ^= 2;
                    fifo32_set(&keycmd, KEYCMD_LED);
                    fifo32_set(&keycmd, key_leds);
                }
                if (data == 256 + 0x46) /* Scrlk Lock */
                {
                    key_leds ^= 1;
                    fifo32_set(&keycmd, KEYCMD_LED);
                    fifo32_set(&keycmd, key_leds);
                }
                if (data == 256 + 0x3b && key_shift != 0 && key_win != 0) /* Shift + F1 */
                {
                    task = key_win->task;
                    if (task != 0 && task->tss.ss0 != 0)
                    {
                        shel_putstr(task->shel, "\nBreak(key) :\n");
                        io_cli();
                        task->tss.eax = (int) &(task->tss.esp0);
                        task->tss.eip = (int) asm_exit_app;
                        io_sti();
                        task_run(task, -1, 0);
                    }
                }
                if (data == 256 + 0x3c && key_shift != 0)   /* Shift + F2 */
                {
                    // 自动将输入焦点切换到新窗口
                    if (key_win != 0)
                        keywin_off(key_win);
                    key_win = open_shell(shtctl, memtotal);
                    sheet_slide(key_win, 32, 4);
                    sheet_updown(key_win, shtctl->top);
                    keywin_on(key_win);
                }
                if (data == 256 + 0xfa) /* get data succesed */
                    keycmd_wait = -1;
                if (data == 256 + 0xfe) /* get data failed */
                {
                    wait_KBC_sendready();
                    io_out8(PORT_KEYDAT, keycmd_wait);
                }
            }
            else if (512 <= data && data <= 767)    /* 鼠标数据 */
            {
                if (mouse_decode(&mdec, data - 512) != 0)
                {
/*                   sprintf(s, "[lcr %4d %4d]", mdec.x, mdec.y);
 *                   if ((mdec.btn & 0x01) != 0) {
 *                       s[1] = 'L';
 *                   }
 *                   if ((mdec.btn & 0x04) != 0) {
 *                       s[2] = 'C';
 *                   }
 *                   if ((mdec.btn & 0x02) != 0) {
 *                       s[3] = 'R';
 *                   }
 *                   putfonts8_asc_sht(sht_back, 32, 16, COL8_FFFFFF, COL8_008484, s, 15);
 */
                    mx += mdec.x;
                    my += mdec.y;
                    if (mx < 0) {
                        mx = 0;
                    }
                    if (my < 0) {
                        my = 0;
                    }
                    if (mx > binfo->scrnx - 1) {
                        mx = binfo->scrnx - 1;
                    }
                    if (my > binfo->scrny - 1) {
                        my = binfo->scrny - 1;
                    }
                    new_mx = mx;
                    new_my = my;
                    if ((mdec.btn & 0x01) != 0)
                    /* 按下左键 */
                    {
                        /* 当mmx为负数时代表当前不处于窗口移动模式 */
                        if (mmx < 0)
                        {
                            /* 如果处于正常模式 */
                            /* 从上到下寻找鼠标所指图层 */
                            for (i = shtctl->top - 1; i > 0; i--)
                            {
                                sht = shtctl->sheets[i];
                                x = mx - sht->vx0;
                                y = my - sht->vy0;
                                if (0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize)
                                {
                                    if (sht->buf[y * sht->bxsize + x] != sht->col_inv)
                                    {
                                        sheet_updown(sht, shtctl->top - 1);
                                        if (sht != key_win)
                                        {
                                            keywin_off(key_win);
                                            key_win = sht;
                                            keywin_on(key_win);
                                        }
                                        if (3 <= x && x < sht->bxsize - 3 && 3 <= y && y < 21)
                                        {
                                            mmx = mx;   //进入窗口移动模式
                                            mmy = my;
                                            mmx2 = sht->vx0;
                                            new_wy = sht->vy0;
                                        }
                                        if (sht->bxsize - 21 <= x && x < sht->bxsize - 5 && 5 <= y && y < 19)
                                        {
                                            /* 点击'x'按钮 */
                                            if ((sht->flags & 0x10) != 0)
                                            {
                                                /* 如果是应用程序窗口 */
                                                task = sht->task;
                                                shel_putstr(task->shel, "\nBreak(mouse) :\n");
                                                io_cli();
                                                task->tss.eax = (int) & (task->tss.esp0);
                                                task->tss.eip = (int) asm_exit_app;
                                                io_sti();
                                                task_run(task, -1, 0);
                                            }
                                            else
                                            {
                                                task = sht->task;
                                                /* 先隐藏窗口 */
                                                sheet_updown(sht, -1);
                                                keywin_off(key_win);
                                                key_win = shtctl->sheets[shtctl->top - 1];
                                                keywin_on(key_win);
                                                io_cli();
                                                fifo32_set(&task->fifo, 4);
                                                io_sti();
                                            }
                                        }
                                        break;
                                    }
                                }
                            }
                        }
                        else
                        {
                            /* 如果处于窗口移动模式 */
                            x = mx - mmx;   //计算鼠标移动距离
                            y = my - mmy;
                            new_wx = (mmx2 + x + 2) & ~3;
                            new_wy = new_wy + y;
                            mmy = my;   //更新为移动后的坐标
                        }
                    }
                    else
                    {
                        /* 未按下左键 */
                        mmx = -1;   //返回正常模式
                        if (new_wx != 0x7fffffff)
                        {
                            sheet_slide(sht, new_wx, new_wy);   //固定图层位置
                            new_wx = 0x7fffffff;
                        }
                    }
                }
            }
            else if (768 <= data && data <= 1023)   //shell窗口关闭处理
            {
                close_shel(shtctl->sheets0 + (data - 768));
            }
            else if (1024 <= data && data <= 2023)
            {
                close_shtask(taskctl->tasks + (data - 1024));
            }
            else if (2024 <= data && data <= 2279)  //只关闭命令行窗口
            {
                struct SHEET *sht = shtctl->sheets0 + (data - 2024);
                memman_free_4k(memman, (int) sht->buf, 256 * 165);
                sheet_free(sht);
            }
        }
    }
}

void keywin_on(struct SHEET *key_win)
{
    change_wtitle(key_win, 1);
    if ((key_win->flags & 0x20) != 0)
        fifo32_set(&key_win->task->fifo, 2);
    return;
}

void keywin_off(struct SHEET *key_win)
{
    change_wtitle(key_win, 0);
    if ((key_win->flags & 0x20) != 0)
        fifo32_set(&key_win->task->fifo, 3);
    return;
}


struct TASK *open_shtask(struct SHEET *sht, unsigned int memtotal)
{
    struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
    struct TASK *task = task_alloc();
    int *shel_fifo = (int *) memman_alloc_4k(memman, 128 * 4);
    task->shel_stack = memman_alloc_4k(memman, 64 * 1024);
    task->tss.esp = task->shel_stack + 64 * 1024 - 12;
    task->tss.eip = (int) &shell_task;
    task->tss.es = 1 * 8;
    task->tss.cs = 2 * 8;
    task->tss.ss = 1 * 8;
    task->tss.ds = 1 * 8;
    task->tss.fs = 1 * 8;
    task->tss.gs = 1 * 8;
    *((int *) (task->tss.esp + 4)) = (int) sht;
    *((int *) (task->tss.esp + 8)) = memtotal;
    task_run(task, 2, 2);   // level=2, priority=2
    fifo32_init(&task->fifo, 128, shel_fifo, task);
    return task;
}

struct SHEET *open_shell(struct SHTCTL *shtctl, unsigned int memtotal)
{
    struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
    struct SHEET *sht = sheet_alloc(shtctl);
    unsigned char *buf = (unsigned char *) memman_alloc_4k(memman, 256 * 165);
    sheet_setbuf(sht, buf, 256, 165, -1);
    make_window8(buf, 256, 165, "shell", 0);
    make_textbox8(sht, 8, 28, 240, 128, COL8_000000);
    sht->task = open_shtask(sht, memtotal);
    sht->flags |= 0x20;      // 有光标
    return sht;
}

void close_shtask(struct TASK *task)
{
    struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
    task_sleep(task);
    memman_free_4k(memman, task->shel_stack, 64 * 1024);
    memman_free_4k(memman, (int) task->fifo.buf, 128 * 4);
    task->flag = TASK_NULL;
    return;
}

void close_shel(struct SHEET *sht)
{
    struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
    struct TASK *task = sht->task;
    memman_free_4k(memman, (int) sht->buf, 256 * 165);
    sheet_free(sht);
    close_shtask(task);
    return;
}
